package gu.simplemq.utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import java.util.concurrent.Callable;

import static gu.simplemq.SimpleLog.log;

/**
 * 后进先出顺序关闭钩子管理器<br>
 * <p>按添加顺序反序执行关闭钩子,确保后添加的先关闭，并且在上一个关闭钩子执行完毕后再执行下一个关闭钩子。</p>
 *  注意：该类是线程安全的。
 * 
 * @author guyadong
 * @since 2.4.0
 */
public class LifoShutdownHooks {
	private static final LinkedHashMap<String, Object> shutdownHooks = new LinkedHashMap<>();
	static {
		// 添加JVM关闭钩子
		// JVM 结束时自动清除所有注册的关闭钩子
		Runtime.getRuntime().addShutdownHook(new Thread(new Thread() {

			@SuppressWarnings("rawtypes")
			@Override
			public void run() {
				ArrayList<Entry<String, Object>> entries = new ArrayList<>(shutdownHooks.entrySet());
				Collections.reverse(entries);
				/** 逆序执行关闭钩子 */ 
				for (Entry<String, Object> entry:entries) {
					try {
						log("Running shutdown hook {}", entry.getKey());
						if (entry.getValue() instanceof Runnable) {
							((Runnable) entry.getValue()).run();
						} else if (entry.getValue() instanceof Callable) {
							((Callable) entry.getValue()).call();
						} else {
							log("Shutdown hook " + entry.getKey() + " is not a Runnable or Callable");
						}
					} catch (Throwable e) {
						e.printStackTrace();
					}
				}
				shutdownHooks.clear();
			}
		}));
	}
	/**
	 * 添加一个关闭钩子
	 * @param name 关闭钩子名称
	 * @param hook 关闭钩子,必须是 Runnable 或 Callable
	 * @throws IllegalArgumentException 如果名称或钩子为null
	 */
	private static synchronized void addShutdownHook0(String name, Object hook) {
		if (name == null || hook == null) {
			throw new IllegalArgumentException("name and hook must not be null");
		}
		if (shutdownHooks.containsKey(name)) {
			throw new IllegalArgumentException("shutdown hook with name " + name + " already exists");
		}
		log("Adding shutdown hook " + name);
		shutdownHooks.put(name, hook);
	}
	/**
	 * 添加一个关闭钩子
	 * @param name 关闭钩子名称
	 * @param hook 关闭钩子
	 * @throws IllegalArgumentException 如果名称或钩子为null
	 */
	public static synchronized void addShutdownHook(String name, Runnable hook) {
		addShutdownHook0(name,hook);
	}
	/**
	 * 添加一个带有返回值的关闭钩子
	 * @param name 关闭钩子名称
	 * @param hook 关闭钩子,返回值将被忽略
	 * @throws IllegalArgumentException 如果名称或钩子为null
	 */
	@SuppressWarnings("rawtypes")
	public static synchronized void addShutdownHook(String name, Callable hook) {
		addShutdownHook0(name,hook);
	}
}
